package com.study.demo.concurrent;

import java.util.concurrent.*;

/**
 * Created by ahdkk on 2016/1/12.
 * test the smartgit.
 */
public class CalcArrayTotal {
    private ExecutorService exec;

    private CompletionService<Long> completionService;

    private int cpuCoreNumber;

    public CalcArrayTotal() {
        //获得处理器
        cpuCoreNumber = Runtime.getRuntime().availableProcessors();
        //创建一个可重用固定线程数的线程池，以共享的无界队列方式来运行这些线程。
        exec = Executors.newFixedThreadPool(cpuCoreNumber);
        //使用为执行基本任务而提供的执行程序创建一个 ExecutorCompletionService，并将 LinkedBlockingQueue 作为完成队列。
        completionService = new ExecutorCompletionService<Long>(exec);
    }

    public Long sum(final int[] numbers) {
        // 根据CPU核心个数拆分任务，创建FutureTask并提交到Executor
        for (int i = 0; i < cpuCoreNumber; i++) {
            int increment = numbers.length / cpuCoreNumber + 1;
            int start = increment * i;
            int end = increment * i + increment;
            if (end > numbers.length) {
                end = numbers.length;
            }
            SumCalculator subCalc = new SumCalculator(numbers, start, end);
            if (!exec.isShutdown()) {
                /**
                 * 生产者 submit() 执行的 任务。使用者 take() 已完成的任务，
                 * 并按照完成这些任务的顺序处理它们的结果  。
                 * 也就是调用CompletionService 的 take 方法是，
                 * 会返回按完成顺序放回任务的结果， CompletionService 内部维护了一个 阻塞队列 BlockingQueue ，
                 * 如果没有任务完成， take() 方法也会阻塞。
                 */
                //submit 在本循环中把小任务提交，并计算，然后把结果放到take()中
                completionService.submit(subCalc);
            }
        }
        return getResult();
    }

    /**
     * 迭代每个只任务，获得部分和，相加返回
     */
    public Long getResult() {
        Long result = 0L;
        //通过循环把分开执行的结果都取回来，然后合并到一起
        for (int i = 0; i < cpuCoreNumber; i++) {
            try {
                Long subSum = completionService.take().get();
                result += subSum;
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    public void close() {
        exec.shutdown();
    }
}
